iT邦幫忙

2024 iThome 鐵人賽

DAY 19
0
JavaScript

30天 JavaScript 提升計畫:從零到精通結合2024年的創新功能系列 第 19

第 19 天:常見的記憶體洩漏問題和解決方法

  • 分享至 

  • xImage
  •  

記憶體洩漏 發生在程式碼中對記憶體的分配後,該記憶體不再被使用,但未被回收,導致記憶體資源浪費。

以下是常見的記憶體洩漏問題以及查資料後的對應解決方法整理。

未解除的事件監聽器


在 DOM 元素上添加事件監聽器,但在元素不再需要時沒有移除這些監聽器,可能會導致記憶體洩漏,因為監聽器持有對 DOM 元素的引用。

解決方法
在不需要時移除事件監聽器,或使用自動化工具檢測未解除的監聽器。

function addEvent() {
  const button = document.getElementById('myButton');
  function handleClick() {
    console.log('Button clicked!');
  }
  button.addEventListener('click', handleClick);
}

// 當你不再需要該事件監聽器時應該移除它
function removeEvent() {
  const button = document.getElementById('myButton');
  function handleClick() {
    console.log('Button clicked!');
  }
  button.removeEventListener('click', handleClick);
}

閉包中的意外引用


閉包可能意外地保持對其外部作用域的引用,這樣會導致外部作用域中的變數無法被垃圾回收。

解決方法
小心設計閉包,避免不必要的引用。

function createClosure() {
  let largeArray = new Array(1000000).fill('memory leak');
  return function() {
    console.log('Closure is still holding on to the large array');
  };
}

const myClosure = createClosure(); // `largeArray` 不會被回收

全局變數


全局變數在變數在整個應用程序的生命周期中都存在所以可以訪問,也因為可能長時間存在於內存中,導致記憶體洩漏。

// 不建議使用全局變數
const globalVariable = 'I am global';

function createLocal() {
  const localVariable = 'I am local';
  console.log(globalVariable); // `globalVariable` 是全局變數
}

解決方法
盡量避免使用全局變數,將變數封裝在局部作用域中,像函式作用域內,或是 ES6 module。

(function() {
  const localConst = 'I am local';
  // 這樣的變數只在函式作用域內可見
})();
// myModule.js
const moduleVar = 'I am module scoped';

export function getModuleVar() {
  return moduleVar;
}

因為我目前使用 Vue 3 Composition API,框架提供了更好的模塊化和組織方式避免全局變數,並更好地管理變數和狀態。

// Vue 3 Composition API example
<script setup>
import { ref } from 'vue';

const localVar = ref('I am local');

function doSomething() {
   console.log(localVar.value);
}
</script>

未清理的定時器和回調函數


如果創建了定時器 setInterval 或異步回調 setTimeout,但是不再需要時沒有清理它們,這些回調函數可能會持續存在,佔用記憶體。

let timer = setInterval(() => {
  console.log('Running...');
}, 1000);

// 沒有停止定時器,可能導致記憶體洩漏
// clearInterval(timer); // 應該在適當的時候停止定時器

解決方法
在不再需要定時器或回調函數時,使用 clearInterval 或其他清理方法來停止它們。

性能優化:


規劃第 25 天會分享 clean_code 在職場上的運用,其中有一個部分是函式的單一作用性,並且提取重複代碼,將重複的代碼提取到共用 function 或 ES module 中!
職場上很常遇到一進頁面就要呼叫 api 然後把 response 開始做邏輯轉換渲染在頁面上,然而有些回傳內容其實是不會改變的,此時可以使用 memoization 等技術減少呼叫 api 可以節省效能以及邏輯轉換時間!

// 不建議
function getData() {
  // 每次調用都進行計算
  return expensiveCalculation();
}

// 改進版
const cache = new Map();

function getData() {
  if (!cache.has('data')) {
    cache.set('data', expensiveCalculation());
  }
  return cache.get('data');
}

上一篇
第 18 天:閉包與作用域
下一篇
第 20 天:JavaScript 工具鏈 - 包管理
系列文
30天 JavaScript 提升計畫:從零到精通結合2024年的創新功能22
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言